package pl.enigmatic.math.graph;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import pl.enigmatic.HasName;
import pl.enigmatic.util.Collections;


public class AttachedLeafsTree<L, B extends HasName, T extends AttachedLeafsTree<L, B, T>> extends NamedBaseTree<B, T> {

	private final boolean allowMultipleAttaching;
	private final List<L> leafs;

	public AttachedLeafsTree(final B base, final boolean allowMultipleAttaching, final List<B> baseChildren, final List<L> leafs) {
		super(base, baseChildren);
		this.leafs = leafs;
		leafs.clear();
		this.allowMultipleAttaching = allowMultipleAttaching;
	}

	public AttachedLeafsTree(final B base, final List<B> baseChildren, final List<L> leafs) {
		this(base, false, baseChildren, leafs);
	}

	public AttachedLeafsTree(final B base, final List<B> baseChildren) {
		this(base, baseChildren, new Vector<L>());
	}

	public AttachedLeafsTree(final B base) {
		this(base, new Vector<B>());
	}

	public List<L> getAttachedLeafs() {
		return leafs;
	}

	protected void onAttachedLeafAdded(final L added) {}

	protected void onAttachedLeafRemoved(final L removed) {}

	public int addAttachedLeaf(final L attachedLeaf, int index) {
		// TODO dodać możliwość dołączania wielokrotnie w węźle
		if (!allowMultipleAttaching && containsAttachedLeaf(attachedLeaf)) { return getAttachedLeafPosition(attachedLeaf); }
		index = Collections.wrap(leafs, index);
		leafs.add(index, attachedLeaf);
		onAttachedLeafAdded(attachedLeaf);
		return index;
	}

	public final int addAttachedLeaf(final L attachedLeaf) {
		return addAttachedLeaf(attachedLeaf, getAttachedLeafsCount());
	}

	public boolean removeAttachedLeaf(final L attachedLeaf) {
		final boolean res = leafs.remove(attachedLeaf);
		if (res) {
			onAttachedLeafRemoved(attachedLeaf);
		}
		return res;
	}

	public final L removeAttachedLeafAt(final int index) {
		final L res = leafs.get(index);
		removeAttachedLeaf(res);
		return res;
	}

	public boolean hasAttachedLeafs() {
		return leafs.size() > 0;
	}

	public int getAttachedLeafsCount() {
		return leafs.size();
	}

	public L getAttachedLeafAt(final int index) {
		return leafs.get(index);
	}

	public boolean containsAttachedLeaf(final L attachedLeaf) {
		return leafs.contains(attachedLeaf);
	}

	public int getAttachedLeafPosition(final L attachedLeaf) {
		return leafs.indexOf(attachedLeaf);
	}

	@SuppressWarnings("unchecked")
	public Set<L> getAllAttachedLeafs(final Set<L> set) {
		set.addAll(leafs);
		if (!isLeaf()) {
			final Iterator<Node<B, T>> i = iterator();
			while (i.hasNext()) {
				((AttachedLeafsTree<L, B, T>) i.next()).getAllAttachedLeafs(set);
			}
		}
		return set;
	}

	public Set<L> getAllAttachedLeafs() {
		return getAllAttachedLeafs(new HashSet<L>());
	}

	@Override
	public String toString() {
		return getClass().getSimpleName() + "[" + super.toString() + ", leafs=" + leafs + "]";
	}
}
